package websocket

import (
	"fmt"
	"io/ioutil"
	stdnet "net"
	"net/http"
	"sync"
	"time"

	"engine/internal"
	net "gitee.com/ameise84/e3net"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

var upgrade = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type wsServer struct {
	ln      stdnet.Listener
	srv     *gin.Engine
	connMap sync.Map
	opts    internal.Options
}

func (ts *wsServer) E3LogMarshall() string {
	return fmt.Sprintf("ws server[%ts]", ts.opts.Addr)
}

func (ts *wsServer) create(opts internal.Options) (err error) {
	gin.DefaultWriter = ioutil.Discard
	gin.SetMode(gin.ReleaseMode)
	ts.srv = gin.Default()
	ts.srv.GET("", ts.onMsg)

	//if !eng_help.IsProduction() {
	//	ts.srv.GET("/debug", ts.onDebugMsg)
	//}

	_readTimeout = time.Duration(opts.ReadTimeout) * time.Second
	_writeTimeout = time.Duration(opts.WriteTimeout) * time.Second
	var ip, port string
	if ip, port, err = net.SplitHostPort(opts.Addr); err != nil {
		return err
	}
	//如果没有指定地址,则只监听外网
	if ip == "" {
		ip = net.GetExternalIP()
	}
	//没有指定端口,默认分配
	if port == "" {
		port = "0"
	}
	opts.Addr = ip + ":" + port
	ts.opts = opts
	return nil
}

func (ts *wsServer) destroy() {
	ts.ln = nil
	ts.srv = nil
}

func (ts *wsServer) start() (addr string, err error) {
	ln, err := stdnet.Listen("tcp", ts.opts.Addr)
	if err != nil {
		return "", err
	}
	ts.ln = ln
	go func() {
		err = ts.srv.RunListener(ts.ln)
	}()

	return ln.Addr().String(), err
}

func (ts *wsServer) stop() {
	_ = ts.ln.Close()
}

func (ts *wsServer) onMsg(c *gin.Context) {
	var cn *websocket.Conn
	{
		var err error
		cn, err = upgrade.Upgrade(c.Writer, c.Request, nil)
		if err != nil {
			c.AbortWithStatus(http.StatusHTTPVersionNotSupported)
			return
		}
	}
	defer func() {
		_ = cn.Close()
	}()
	rc := takeRawContext()
	netActor, ok := _gActorServer.StartNetActor(rc.id)
	if !ok {
		return
	}
	rc.onInit(cn, netActor)
loopRead:
	for {
		if _readTimeout > 0 {
			_ = cn.SetReadDeadline(time.Now().Add(_readTimeout))
		}
		mk, msg, err := cn.ReadMessage()
		if err != nil {
			break loopRead
		}
		switch mk {
		case websocket.BinaryMessage:
			if err = rc.onRecv(msg); err != nil {
				break loopRead
			}
		case websocket.CloseMessage:
			break loopRead
		case websocket.PingMessage:
			if _writeTimeout > 0 {
				_ = cn.SetWriteDeadline(time.Now().Add(_writeTimeout))
			}
			if err = cn.WriteMessage(websocket.PongMessage, nil); err != nil {
				break loopRead
			}
		default:
			break loopRead
		}
	}
	rc.wg.Wait()
	rc.onClosed()
	freeRawContext(rc)
}
